<!DOCTYPE html>
<html lang="zh-CN">
<!--浏览器搞笑标题-->
<script type="text/javascript" src="/js/FunnyTitle.js"></script>






<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/binblog/img/binblog.ico">
  <link rel="icon" type="image/png" href="/binblog/img/binblog.ico">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="description" content="">
  <meta name="author" content="leo-bin">
  <meta name="keywords" content="">
  <title>手写系列之手写一个MyBatis - 阿斌的个人博客</title>

  <link  rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.staticfile.org/github-markdown-css/4.0.0/github-markdown.min.css" />
  <link  rel="stylesheet" href="/binblog/lib/hint/hint.min.css" />

  
    <link  rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.0.0/styles/dracula.min.css" />
  

  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_yg9cfy8wd6.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_pjno9b9zyxs.css">


<link  rel="stylesheet" href="/binblog/css/main.css" />

<!-- 自定义样式保持在最底部 -->


<meta name="generator" content="Hexo 4.2.1"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand"
       href="/binblog/">&nbsp;<strong>Abin的Blog</strong>&nbsp;</a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/binblog/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/binblog/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/binblog/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/binblog/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/binblog/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" data-toggle="modal" data-target="#modalSearch">&nbsp;&nbsp;<i
                class="iconfont icon-search"></i>&nbsp;&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="view intro-2" id="background" parallax=true
         style="background: url('/binblog/img/6.webp') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="container text-center white-text fadeInUp">
            <span class="h2" id="subtitle">
              
            </span>

            
              
  <div class="mt-3 post-meta">
    <i class="iconfont icon-date-fill" aria-hidden="true"></i>
    <time datetime="2020-05-31 22:19">
      2020年5月31日 晚上
    </time>
  </div>


<div class="mt-1">
  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      3.3k 字
    </span>
  

  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      51
       分钟
    </span>
  

  
  
    
      <!-- LeanCloud 统计文章PV -->
      <span id="leancloud-post-views-container" class="post-meta" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="leancloud-post-views"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid">
  <div class="row">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-md">
      <div class="container nopadding-md" id="board-ctn">
        <div class="py-5" id="board">
          <div class="post-content mx-auto" id="post">
            
              <p class="note note-info">
                
                  本文最后更新于：2020年7月19日 晚上
                
              </p>
            
            <article class="markdown-body">
              <h1 id="手写系列之手写一个MyBatis"><a href="#手写系列之手写一个MyBatis" class="headerlink" title="手写系列之手写一个MyBatis"></a>手写系列之手写一个MyBatis</h1><h2 id="前话"><a href="#前话" class="headerlink" title="前话"></a>前话</h2><p>既然知道了 MyBatis的底层原理了，那么不妨动手实现一个简易版<strong>MyBatis</strong>！</p>
<p>通过仿照MyBatis的实现原理，进一步的加深对MyBatis框架的理解。</p>
<p>限于本人的水平，代码写的比较简陋，误喷。</p>
<p><strong><code>仓库地址：</code></strong>  <a href="https://github.com/leo-bin/binbatis" target="_blank" rel="noopener">https://github.com/leo-bin/binbatis</a></p>
<h2 id="项目设计难点"><a href="#项目设计难点" class="headerlink" title="项目设计难点"></a>项目设计难点</h2><p><strong>1.如何使用接口和sql的映射</strong></p>
<p>在平常的MyBatis使用中，我们会发现我们明明只是写了一个接口。</p>
<p>这个接口中有我们要用到的方法，但是没有实现。</p>
<p>然后我们还在xml文件中写了部分sql，但是也不完整。</p>
<p>然后我们会发现在实际使用过程中，一个mapper实例竟然可以直接操作数据库。</p>
<p>这个过程就用到了<strong>JDK动态代理</strong>。</p>
<p>通过动态代理去实现我们的接口，当调用接口时，实际上是使用的代理类。</p>
<p>那么代理类就只需要拿到具体的调用参数，底层使用SqlSession来完成数据库的操作就行。</p>
<p><br></p>
<p><strong>2.配置文件的读取以及如何对JDBC进行封装</strong></p>
<p>其实配置文件的读取好说，但是如何通过对JDBC进行封装从而实现对数据库的操作呢？</p>
<p>这里就需要看源码的实现了，源码中写了一系列的通用操作来实现常用SQL的调用。</p>
<p>比如说查询，就是使用的select接口。</p>
<p>修改，删除，增加，都使用的update接口。</p>
<h2 id="项目实现"><a href="#项目实现" class="headerlink" title="项目实现"></a>项目实现</h2><p><strong>架构图：</strong></p>
<p><img src="https://bins-pic.oss-cn-shanghai.aliyuncs.com/mypic/Y8sCLj.jpg" srcset="/binblog/img/loading.gif" alt="Y8sCLj.jpg" style="zoom:200%;" /></p>
<p><br></p>
<p><strong>大概流程就是：</strong></p>
<p>1.首先是读配置文件：jdbc.properties,mapper.xml文件，将之封装到bean中。</p>
<p>2.通过生成的Configuration来构造一个SqlSessonFacotry。</p>
<p>3.然后生成一个Executor同样也将之放入SqlSessonFactory。</p>
<p>4.用户这边通过会话工厂拿到一个SqlSesson。</p>
<p>5.通过将对应的Mapper接口传给SqlSesson，返回一个代理对象。</p>
<p>6.这个代理对象本质上就是一个持有SqlSesson的对象。</p>
<p>7.当用户调用代理对象时，将参数传给代理对象。</p>
<p>8.代理对象拿着参数根据传过来的方法类型调用SqlSesson中的查询或者修改方法。</p>
<p>9.SqlSesson调用方法本质上是通过Executor来实现的。</p>
<p>10.而Executor本质上就是通过封装JDBC的基本操作来实现的。</p>
<p>11.方法调用完之后，将原始结果集合转换为对应的bean返回给用户。</p>
<p>12.至此结束调用，关闭连接。</p>
<h3 id="1）实现细节"><a href="#1）实现细节" class="headerlink" title="1）实现细节"></a>1）实现细节</h3><p>配置文件的读取和封装：</p>
<p>我们通过读取配置文件将之封装到配置对象中：</p>
<p><strong>MappedStatement对象：</strong></p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MappedStatement</span> </span>&#123;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * sql字段所在的命名空间</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String namespace;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * sql字段的id</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String id;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * Sql执行结果类型的全限定名</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String resultType;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 具体的sql语句</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String sql;

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getNamespace</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> namespace;
    &#125;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setNamespace</span><span class="hljs-params">(String namespace)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.namespace = namespace;
    &#125;
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> id;
    &#125;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(String id)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.id = id;
    &#125;
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getResultType</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> resultType;
    &#125;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setResultType</span><span class="hljs-params">(String resultType)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.resultType = resultType;
    &#125;
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getSql</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> sql;
    &#125;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setSql</span><span class="hljs-params">(String sql)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.sql = sql;
    &#125;
&#125;</code></pre></div>
<p>Configuration对象：</p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Configuration</span> </span>&#123;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * jdbc驱动</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String jdbcDriver;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * jdbc url</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String jdbcUrl;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * userName</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String jdbcUserName;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * passWord</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String jdbcPassWord;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 存放所有的Sql映射文件中的sql配置</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> Map&lt;String ,MappedStatement&gt; mappedStatements=<span class="hljs-keyword">new</span> HashMap&lt;&gt;(<span class="hljs-number">16</span>);


    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getJdbcDriver</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> jdbcDriver;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setJdbcDriver</span><span class="hljs-params">(String jdbcDriver)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.jdbcDriver = jdbcDriver;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getJdbcUrl</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> jdbcUrl;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setJdbcUrl</span><span class="hljs-params">(String jdbcUrl)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.jdbcUrl = jdbcUrl;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getJdbcUserName</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> jdbcUserName;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setJdbcUserName</span><span class="hljs-params">(String jdbcUserName)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.jdbcUserName = jdbcUserName;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getJdbcPassWord</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> jdbcPassWord;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setJdbcPassWord</span><span class="hljs-params">(String jdbcPassWord)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.jdbcPassWord = jdbcPassWord;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> Map&lt;String, MappedStatement&gt; <span class="hljs-title">getMappedStatements</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> mappedStatements;
    &#125;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setMappedStatements</span><span class="hljs-params">(Map&lt;String, MappedStatement&gt; mappedStatements)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.mappedStatements = mappedStatements;
    &#125;
&#125;</code></pre></div>
<p><br></p>
<p><strong>Executor实例接口：</strong></p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Executor</span> </span>&#123;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 查询接口</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> mappedStatement mappers.xml中的sql字段&lt;select&gt;***&lt;/select&gt;</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter       查询参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> &lt;E&gt;             返回结果</span>
<span class="hljs-comment">     */</span>
    &lt;E&gt; <span class="hljs-function">List&lt;E&gt; <span class="hljs-title">query</span><span class="hljs-params">(MappedStatement mappedStatement, Object parameter)</span></span>;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 修改</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> mappedStatement 修改sql</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter       修改参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 修改影响结果</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@throws</span> SQLException</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">update</span><span class="hljs-params">(MappedStatement mappedStatement, Object parameter)</span> <span class="hljs-keyword">throws</span> SQLException</span>;
&#125;</code></pre></div>
<p><br></p>
<p><strong>实现类：</strong>其实就是封装了JDBC的操作。</p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimpleExecutor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Executor</span> </span>&#123;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 查询</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> mappedStatement mappers.xml中的sql字段&lt;select&gt;***&lt;/select&gt;</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter       查询参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> &lt;E&gt;             泛型</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 结果</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> &lt;E&gt; <span class="hljs-function">List&lt;E&gt; <span class="hljs-title">query</span><span class="hljs-params">(MappedStatement mappedStatement, Object parameter)</span> </span>&#123;
        System.out.println(mappedStatement.getSql());

        List&lt;E&gt; results = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
        PreparedStatement preparedStatement = <span class="hljs-keyword">null</span>;
        ResultSet resultSet = <span class="hljs-keyword">null</span>;

        <span class="hljs-comment">//1.拿到数据库连接实例</span>
        Connection connection = JdbcUtil.getConnection();
        <span class="hljs-comment">//2.sql语句预编译，并且用?占位,然后将具体的参数set进去</span>
        Map&lt;String, String&gt; map = SqlParserUtil.parse(mappedStatement.getSql());
        String sql = map.get(<span class="hljs-string">"sql"</span>);
        <span class="hljs-keyword">try</span> &#123;
            preparedStatement = connection.prepareStatement(sql);
            setParameter(preparedStatement, parameter, map);
            <span class="hljs-comment">//3.执行查询</span>
            resultSet = preparedStatement.executeQuery();
            <span class="hljs-comment">//4.处理sql执行结果</span>
            handlerResultSet(resultSet, results, mappedStatement.getResultType());
        &#125; <span class="hljs-keyword">catch</span> (SQLException e) &#123;
            e.printStackTrace();
        &#125; <span class="hljs-keyword">finally</span> &#123;
            <span class="hljs-comment">//5.最后关闭连接</span>
            JdbcUtil.close(connection, preparedStatement, resultSet);
        &#125;
        <span class="hljs-keyword">return</span> results;
    &#125;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 修改操作</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> mappedStatement 修改sql</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter       修改参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 修改影响结果</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@throws</span> SQLException sql异常</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">update</span><span class="hljs-params">(MappedStatement mappedStatement, Object parameter)</span> <span class="hljs-keyword">throws</span> SQLException </span>&#123;
        System.out.println(mappedStatement.getSql());

        PreparedStatement preparedStatement = <span class="hljs-keyword">null</span>;
        ResultSet resultSet = <span class="hljs-keyword">null</span>;

        <span class="hljs-comment">//1.拿到connection实例</span>
        Connection connection = JdbcUtil.getConnection();
        <span class="hljs-comment">//2.得到预处理之后的sql</span>
        Map&lt;String, String&gt; map = SqlParserUtil.parse(mappedStatement.getSql());
        String sql = map.get(<span class="hljs-string">"sql"</span>);
        <span class="hljs-keyword">try</span> &#123;
            preparedStatement = connection.prepareStatement(sql);
            <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> BY leo-bin 2020/5/9</span>
            <span class="hljs-comment">// TODO-LIST: 完成update方法</span>
        &#125; <span class="hljs-keyword">catch</span> (SQLException e) &#123;
            e.printStackTrace();
        &#125;
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    &#125;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 处理占位符</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> preparedStatement 预处理sql语句对象</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter         参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@throws</span> SQLException sql异常</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setParameter</span><span class="hljs-params">(PreparedStatement preparedStatement, Object parameter, Map&lt;String, String&gt; map)</span></span>
<span class="hljs-function">            <span class="hljs-keyword">throws</span> SQLException </span>&#123;
        <span class="hljs-keyword">if</span> (parameter <span class="hljs-keyword">instanceof</span> Integer) &#123;
            preparedStatement.setInt(<span class="hljs-number">1</span>, (<span class="hljs-keyword">int</span>) parameter);
        &#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (parameter <span class="hljs-keyword">instanceof</span> String) &#123;
            preparedStatement.setString(<span class="hljs-number">1</span>, (String) parameter);
        &#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (parameter <span class="hljs-keyword">instanceof</span> Long) &#123;
            preparedStatement.setLong(<span class="hljs-number">1</span>, (Long) parameter);
        &#125;
    &#125;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 处理结果</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> resultSet sql执行结果</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> results   返回结果</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> className 类名</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> &lt;E&gt;       bean的type</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-meta">@SuppressWarnings</span>(<span class="hljs-string">"unchecked"</span>)
    <span class="hljs-keyword">private</span> &lt;E&gt; <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">handlerResultSet</span><span class="hljs-params">(ResultSet resultSet, List&lt;E&gt; results, String className)</span> </span>&#123;
        Class&lt;E&gt; clazz;
        <span class="hljs-comment">//通过反射拿到具体的类对象</span>
        <span class="hljs-keyword">try</span> &#123;
            clazz = (Class&lt;E&gt;) Class.forName(className);
            <span class="hljs-keyword">while</span> (resultSet.next()) &#123;
                Object entity = clazz.newInstance();
                <span class="hljs-comment">//resultSet-&gt;entity</span>
                ReflectionUtil.resultSet2Bean(entity, resultSet);
                results.add((E) entity);
            &#125;
        &#125; <span class="hljs-keyword">catch</span> (ClassNotFoundException | IllegalAccessException | InstantiationException | SQLException e) &#123;
            e.printStackTrace();
        &#125;
    &#125;
&#125;</code></pre></div>
<p><strong>单个会话实例：</strong>里面封装了基本的对数据库的CRUD操作</p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">SqlSession</span> </span>&#123;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 根据传入的条件查询单一结果</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> statement 方法对应 sql 语句，namespace+id</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter 要传入 sql 语句中的查询参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> &lt;T&gt;       返回指定的结果对象</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 结果集</span>
<span class="hljs-comment">     */</span>
    &lt;T&gt; <span class="hljs-function">T <span class="hljs-title">selectOne</span><span class="hljs-params">(String statement, Object parameter)</span></span>;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 返回结果集合</span>
<span class="hljs-comment">     */</span>
    &lt;T&gt; <span class="hljs-function">List&lt;T&gt; <span class="hljs-title">selectList</span><span class="hljs-params">(String statement, Object parameter)</span></span>;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 插入</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> statement 插入sql</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter sql参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 插入的行数</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">insert</span><span class="hljs-params">(String statement, Object parameter)</span></span>;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 修改</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> statement 修改sql</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter 参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 修改时影响的行数</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">update</span><span class="hljs-params">(String statement, Object parameter)</span></span>;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 删除</span>
<span class="hljs-comment">     *</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> statement 删除sql</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@param</span> parameter 参数</span>
<span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 删除之后影响的行数</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">delete</span><span class="hljs-params">(String statement, Object parameter)</span></span>;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 得到一个mapper接口的实现类</span>
<span class="hljs-comment">     */</span>
    &lt;T&gt; <span class="hljs-function">T <span class="hljs-title">getMapper</span><span class="hljs-params">(Class&lt;T&gt; type)</span></span>;
&#125;</code></pre></div>
<p><br></p>
<p><strong>实现类：</strong>这里只是实现了查询的接口，剩下的可以自己扩展实现</p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DefaultSqlSession</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">SqlSession</span> </span>&#123;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Configuration configuration;
    <span class="hljs-keyword">private</span> Executor executor;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">DefaultSqlSession</span><span class="hljs-params">(Configuration configuration)</span> </span>&#123;
        <span class="hljs-keyword">super</span>();
        <span class="hljs-keyword">this</span>.configuration = configuration;
        executor = <span class="hljs-keyword">new</span> SimpleExecutor();
    &#125;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">T <span class="hljs-title">selectOne</span><span class="hljs-params">(String statement, Object parameter)</span> </span>&#123;
        List&lt;T&gt; list = <span class="hljs-keyword">this</span>.&lt;T&gt;selectList(statement, parameter);
        <span class="hljs-keyword">if</span> (list.size() == <span class="hljs-number">1</span>) &#123;
            <span class="hljs-keyword">return</span> list.get(<span class="hljs-number">0</span>);
        &#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (list.size() &gt; <span class="hljs-number">1</span>) &#123;
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"too many results!"</span>);
        &#125; <span class="hljs-keyword">else</span> &#123;
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        &#125;
    &#125;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">List&lt;T&gt; <span class="hljs-title">selectList</span><span class="hljs-params">(String statement, Object parameter)</span> </span>&#123;
        MappedStatement mappedStatement = configuration.getMappedStatements().get(statement);
        <span class="hljs-keyword">return</span> executor.query(mappedStatement, parameter);
    &#125;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">insert</span><span class="hljs-params">(String statement, Object parameter)</span> </span>&#123;
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    &#125;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">update</span><span class="hljs-params">(String statement, Object parameter)</span> </span>&#123;
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    &#125;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">delete</span><span class="hljs-params">(String statement, Object parameter)</span> </span>&#123;
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    &#125;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">T <span class="hljs-title">getMapper</span><span class="hljs-params">(Class&lt;T&gt; type)</span> </span>&#123;
        MapperProxyFactory proxyFactory = <span class="hljs-keyword">new</span> MapperProxyFactory();
        <span class="hljs-keyword">return</span> proxyFactory.newInstance(type, <span class="hljs-keyword">this</span>);
    &#125;
&#125;</code></pre></div>
<p><br></p>
<p><strong>ok,现在开始创建会话工厂：</strong></p>
<p><strong>接口：</strong></p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">SqlSessionFactory</span> </span>&#123;
    <span class="hljs-function">SqlSession <span class="hljs-title">openSession</span><span class="hljs-params">()</span></span>;
&#125;</code></pre></div>
<p><strong>实现类：</strong></p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DefaultSqlSessionFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">SqlSessionFactory</span> </span>&#123;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 全局配置对象</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Configuration configuration = <span class="hljs-keyword">new</span> Configuration();
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * mappers.xml配置文件位置</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String MAPPER_CONFIG_LOCATION = <span class="hljs-string">"mappers"</span>;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 数据库配置文件</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String DB_CONFIG_FILE = <span class="hljs-string">"jdbc.properties"</span>;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">DefaultSqlSessionFactory</span><span class="hljs-params">()</span> </span>&#123;
        loadDBInfo();
        loadMapperInfo();
        JdbcUtil.init(configuration);
    &#125;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 加载db配置信息</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">loadDBInfo</span><span class="hljs-params">()</span> </span>&#123;
        InputStream dbStream = <span class="hljs-keyword">this</span>.getClass().getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
        Properties properties = <span class="hljs-keyword">new</span> Properties();
        <span class="hljs-keyword">try</span> &#123;
            properties.load(dbStream);
        &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
            e.printStackTrace();
        &#125;
        configuration.setJdbcDriver(properties.getProperty(<span class="hljs-string">"jdbc.driver"</span>));
        configuration.setJdbcUrl(properties.getProperty(<span class="hljs-string">"jdbc.url"</span>));
        configuration.setJdbcUserName(properties.getProperty(<span class="hljs-string">"jdbc.username"</span>));
        configuration.setJdbcPassWord(properties.getProperty(<span class="hljs-string">"jdbc.password"</span>));
    &#125;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 加载mappers文件夹下的所有mapper xml文件</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">loadMapperInfo</span><span class="hljs-params">()</span> </span>&#123;
        String filePath = <span class="hljs-keyword">null</span>;
        <span class="hljs-keyword">try</span> &#123;
            URL url = <span class="hljs-keyword">this</span>.getClass().getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
            <span class="hljs-keyword">if</span> (url != <span class="hljs-keyword">null</span>) &#123;
                filePath = URLDecoder.decode(url.getPath(), <span class="hljs-string">"UTF-8"</span>);
            &#125;
        &#125; <span class="hljs-keyword">catch</span> (UnsupportedEncodingException e) &#123;
            e.printStackTrace();
        &#125;
        <span class="hljs-keyword">if</span> (filePath != <span class="hljs-keyword">null</span>) &#123;
            File mappers = <span class="hljs-keyword">new</span> File(filePath);
            File[] files = mappers.listFiles();
            <span class="hljs-keyword">if</span> (files != <span class="hljs-keyword">null</span>) &#123;
                <span class="hljs-keyword">for</span> (File file : files) &#123;
                    loadMapperInfo(file);
                &#125;
            &#125;
        &#125;
    &#125;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 加载并解析每一个mapper xml文件</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">loadMapperInfo</span><span class="hljs-params">(File file)</span> </span>&#123;
        SAXReader reader = <span class="hljs-keyword">new</span> SAXReader();
        Document document = <span class="hljs-keyword">null</span>;
        <span class="hljs-keyword">try</span> &#123;
            document = reader.read(file);
        &#125; <span class="hljs-keyword">catch</span> (DocumentException e) &#123;
            e.printStackTrace();
        &#125;
        <span class="hljs-comment">//拿到根节点元素&lt;mappers&gt;</span>
        Element root = document.getRootElement();
        <span class="hljs-comment">//从mapper元素中拿namespace</span>
        String namespace = root.attribute(<span class="hljs-string">"namespace"</span>).getData().toString();
        <span class="hljs-comment">//获取select，insert，update，delete字段的节点集合</span>
        List&lt;Element&gt; selects = root.elements(<span class="hljs-string">"select"</span>);
        List&lt;Element&gt; inserts = root.elements(<span class="hljs-string">"insert"</span>);
        List&lt;Element&gt; updates = root.elements(<span class="hljs-string">"update"</span>);
        List&lt;Element&gt; deletes = root.elements(<span class="hljs-string">"delete"</span>);
        List&lt;Element&gt; all = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
        <span class="hljs-keyword">if</span> (!CDUtil.isEmptyList(selects)) &#123;
            all.addAll(selects);
        &#125;
        <span class="hljs-keyword">if</span> (!CDUtil.isEmptyList(inserts)) &#123;
            all.addAll(inserts);
        &#125;
        <span class="hljs-keyword">if</span> (!CDUtil.isEmptyList(updates)) &#123;
            all.addAll(updates);
        &#125;
        <span class="hljs-keyword">if</span> (!CDUtil.isEmptyList(deletes)) &#123;
            all.addAll(deletes);
        &#125;
        <span class="hljs-keyword">for</span> (Element element : all) &#123;
            MappedStatement mappedStatement = <span class="hljs-keyword">new</span> MappedStatement();
            String id = element.attribute(<span class="hljs-string">"id"</span>).getData().toString();
            Attribute attribute = element.attribute(<span class="hljs-string">"resultType"</span>);
            String resultType = attribute != <span class="hljs-keyword">null</span> ? attribute.getData().toString() : <span class="hljs-keyword">null</span>;
            String sql = element.getData().toString();
            mappedStatement.setId(id);
            mappedStatement.setNamespace(namespace);
            mappedStatement.setResultType(resultType);
            mappedStatement.setSql(sql);
            configuration.getMappedStatements().put(namespace + <span class="hljs-string">"."</span> + id, mappedStatement);
        &#125;
    &#125;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> SqlSession <span class="hljs-title">openSession</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DefaultSqlSession(configuration);
    &#125;
&#125;</code></pre></div>
<p><br></p>
<p><strong>接口是我们的动态代理的实现：</strong></p>
<p><strong>我们写一个代理工厂类，方便生成具体的代理：</strong></p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MapperProxyFactory</span> </span>&#123;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 根据type生成具体的代理类</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-meta">@SuppressWarnings</span>(<span class="hljs-string">"unchecked"</span>)
    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">T <span class="hljs-title">newInstance</span><span class="hljs-params">(Class&lt;T&gt; type, SqlSession sqlSession)</span> </span>&#123;
        MapperProxy proxy = <span class="hljs-keyword">new</span> MapperProxy(sqlSession);
        <span class="hljs-keyword">return</span> (T) Proxy.newProxyInstance(type.getClassLoader(), <span class="hljs-keyword">new</span> Class[]&#123;type&#125;, proxy);
    &#125;
&#125;</code></pre></div>
<p><strong>MapperProxy是我们需要提前写好的加强方法，这里就是调用SqlSesson：</strong></p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MapperProxy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">InvocationHandler</span> </span>&#123;
    <span class="hljs-keyword">private</span> SqlSession sqlSession;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">MapperProxy</span><span class="hljs-params">(SqlSession sqlSession)</span> </span>&#123;
        <span class="hljs-keyword">this</span>.sqlSession = sqlSession;
    &#125;

    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 拦截实际调用的方法并作出转发</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">invoke</span><span class="hljs-params">(Object proxy, Method method, Object[] args)</span> <span class="hljs-keyword">throws</span> Throwable </span>&#123;
        System.out.println(method.getDeclaringClass().getName() + <span class="hljs-string">"."</span> + method.getName());
        <span class="hljs-comment">//根据返回类型判断是查询单个结果还是集合结果</span>
        <span class="hljs-keyword">if</span> (Collection<span class="hljs-class">.<span class="hljs-keyword">class</span>.<span class="hljs-title">isAssignableFrom</span>(<span class="hljs-title">method</span>.<span class="hljs-title">getReturnType</span>())) </span>&#123;
            <span class="hljs-keyword">return</span> sqlSession.selectList(method
                    .getDeclaringClass().getName()
                    + <span class="hljs-string">"."</span>
                    + method.getName(), args == <span class="hljs-keyword">null</span> ? <span class="hljs-keyword">null</span> : args[<span class="hljs-number">0</span>]);
        &#125; <span class="hljs-keyword">else</span> &#123;
            <span class="hljs-keyword">return</span> sqlSession.selectOne(method
                    .getDeclaringClass().getName()
                    + <span class="hljs-string">"."</span>
                    + method.getName(), args == <span class="hljs-keyword">null</span> ? <span class="hljs-keyword">null</span> : args[<span class="hljs-number">0</span>]);
        &#125;
    &#125;
&#125;</code></pre></div>
<p><br></p>
<p><strong>这个写好之后的骨架：</strong></p>
<p><img src="https://bins-pic.oss-cn-shanghai.aliyuncs.com/mypic/image-20200719204850763.png" srcset="/binblog/img/loading.gif" alt="image-20200719204850763"></p>
<p><br></p>
<p>我们另外创建一个模块，叫做测试模块：</p>
<p>然后我们写好基本的实体和接口：</p>
<p><strong>实体：</strong></p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-meta">@Data</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserInfo</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>&#123;
    <span class="hljs-keyword">private</span> Integer id;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 用户名</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String userName;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 密码</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> String passWord;
    <span class="hljs-comment">/**</span>
<span class="hljs-comment">     * 注册时间</span>
<span class="hljs-comment">     */</span>
    <span class="hljs-keyword">private</span> Date regDate;

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> serialVersionUID = <span class="hljs-number">1L</span>;
&#125;</code></pre></div>
<p><br></p>
<p><strong>接口：</strong></p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserInfoMapper</span> </span>&#123;
    <span class="hljs-function">UserInfo <span class="hljs-title">selectByPrimaryKey</span><span class="hljs-params">(Integer id)</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">insert</span><span class="hljs-params">(UserInfo userInfo)</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">delete</span><span class="hljs-params">(Integer id)</span></span>;
&#125;</code></pre></div>
<p><br></p>
<p><strong>仿照mapper.xml文件也写一个xml文件存sql：</strong></p>
<div class="hljs"><pre><code class="hljs xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">mapper</span> <span class="hljs-attr">namespace</span>=<span class="hljs-string">"com.bins.test.dao.UserInfoMapper"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">insert</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"insert"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"com.bins.test.pojo.UserInfo"</span>&gt;</span>
        insert into user_info(user_name, pass_word) VALUES (#&#123;userName&#125;,#&#123;passWord&#125;)
    <span class="hljs-tag">&lt;/<span class="hljs-name">insert</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">delete</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"delete"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"java.lang.Integer"</span>&gt;</span>
        delete from user_info where id=#&#123;id&#125;
    <span class="hljs-tag">&lt;/<span class="hljs-name">delete</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"selectByPrimaryKey"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"java.lang.Integer"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.bins.test.pojo.UserInfo"</span>&gt;</span>
        select *
        from user_info
        where id = #&#123;id&#125;
    <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">mapper</span>&gt;</span></code></pre></div>
<p><br></p>
<p><strong>然后我们提前创建数据库和相应的表：</strong></p>
<div class="hljs"><pre><code class="hljs sql"><span class="hljs-comment">#创建数据库</span>
<span class="hljs-keyword">create</span> <span class="hljs-keyword">database</span> bin_batis;

<span class="hljs-keyword">use</span> bin_batis;

<span class="hljs-comment"># 建用户表</span>
<span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> user_info
(
  <span class="hljs-keyword">id</span>       <span class="hljs-built_in">int</span> auto_increment primary <span class="hljs-keyword">key</span>,
  username <span class="hljs-built_in">varchar</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">comment</span> <span class="hljs-string">'用户名'</span>,
  <span class="hljs-keyword">password</span> <span class="hljs-built_in">varchar</span>(<span class="hljs-number">12</span>) <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">comment</span> <span class="hljs-string">'密码'</span>,
  reg_date <span class="hljs-built_in">timestamp</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span> <span class="hljs-keyword">comment</span> <span class="hljs-string">'注册时间'</span>
)</code></pre></div>
<p><br></p>
<p><strong>然后插入一些数据：</strong></p>
<p><img src="https://bins-pic.oss-cn-shanghai.aliyuncs.com/mypic/image-20200719205625416.png" srcset="/binblog/img/loading.gif" alt="image-20200719205625416"></p>
<p><br></p>
<p><strong>当然，不能忘记JDBC的配置文件，否则怎么连数据库呢：</strong></p>
<div class="hljs"><pre><code class="hljs properties"><span class="hljs-meta">jdbc.driver</span>=<span class="hljs-string">com.mysql.cj.jdbc.Driver</span>
<span class="hljs-meta">jdbc.url</span>=<span class="hljs-string">jdbc:mysql://localhost:3306/bin_batis?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span>
<span class="hljs-meta">jdbc.username</span>=<span class="hljs-string">root</span>
<span class="hljs-meta">jdbc.password</span>=<span class="hljs-string">115118</span></code></pre></div>
<p><br></p>
<p><strong>因为使用的maven创建的项目，所以要想使用上面的MyBatis项目需要在pom文件指定：</strong></p>
<div class="hljs"><pre><code class="hljs java">&lt;?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span>?&gt;
&lt;project xmlns=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span>
         xmlns:xsi=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>
         xsi:schemaLocation=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

    &lt;groupId&gt;com.bins.mybatis.test&lt;/groupId&gt;
    &lt;artifactId&gt;test-mybatis&lt;/artifactId&gt;
    &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;

    &lt;dependencies&gt;
        &lt;!--单元测试--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;junit&lt;/groupId&gt;
            &lt;artifactId&gt;junit&lt;/artifactId&gt;
            &lt;version&gt;4.11&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
            &lt;version&gt;1.18.10&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!--bin-batis--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.bins.mybatis&lt;/groupId&gt;
            &lt;artifactId&gt;bin-batis&lt;/artifactId&gt;
            &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/project&gt;</code></pre></div>
<p><br></p>
<p><strong>ok，最后我们写一个测试类：</strong>(这里用的单元测试)</p>
<div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserInfoMapperTest</span> </span>&#123;
    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testSelectById</span><span class="hljs-params">()</span> </span>&#123;
        <span class="hljs-keyword">int</span> id = <span class="hljs-number">1</span>;
        <span class="hljs-comment">//创建会话工厂，加载配置文件</span>
        SqlSessionFactory sqlSessionFactory = <span class="hljs-keyword">new</span> DefaultSqlSessionFactory();
        <span class="hljs-comment">//拿到单个会话实例</span>
        SqlSession sqlSession = sqlSessionFactory.openSession();
        <span class="hljs-comment">//通过单个会话实例生成目标的代理对象</span>
        UserInfoMapper userInfoMapper = sqlSession.getMapper(UserInfoMapper<span class="hljs-class">.<span class="hljs-keyword">class</span>)</span>;
        <span class="hljs-comment">//执行代理的方法</span>
        UserInfo userInfo = userInfoMapper.selectByPrimaryKey(id);
        System.out.println(userInfo);
    &#125;
&#125;</code></pre></div>
<p><br></p>
<p><strong>测试结果：</strong></p>
<p><img src="https://bins-pic.oss-cn-shanghai.aliyuncs.com/mypic/image-20200719205809919.png" srcset="/binblog/img/loading.gif" alt="image-20200719205809919"></p>
<p><br></p>
<p><strong>其实还用到了一些工具类没放出来，大家可以看看具体的项目实现：</strong></p>
<h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p><strong>项目实现的比较简陋，只写了基本的查询功能。</strong></p>
<p><strong>但是对于理解MyBatis的整体流程还是很有帮助的。</strong></p>
<p><strong>欢迎大家学习和交流（不妨点个Star⭐）</strong></p>

            </article>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/binblog/categories/ORM%E6%A1%86%E6%9E%B6/">ORM框架</a>
                    
                  </div>
                
                
              </div>
              
                <p class="note note-warning">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" target="_blank" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！</p>
              
              
                <div class="post-prevnext row">
                  <div class="post-prev col-6">
                    
                    
                      <a href="/binblog/2020/05/31/%E5%87%A0%E7%A7%8D%E5%B8%B8%E8%A7%81%E7%9A%84%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">几种常见的负载均衡算法</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </div>
                  <div class="post-next col-6">
                    
                    
                      <a href="/binblog/2020/05/31/%E6%89%8B%E5%86%99%E7%B3%BB%E5%88%97%E4%B9%8B%E6%89%8B%E5%86%99%E4%B8%80%E4%B8%AA%E4%BA%8C%E5%8F%89%E5%A0%86/">
                        <span class="hidden-mobile">手写系列之手写一个二叉堆</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </div>
                </div>
              
            </div>

            
              <!-- Comments -->
              <div class="comments" id="comments">
                
                
  <div id="vcomments"></div>
  <script defer src="https://cdn.staticfile.org/valine/1.4.14/Valine.min.js" ></script>

  <script type="text/javascript">
    var oldLoadVa = window.onload;
    window.onload = function () {
      oldLoadVa && oldLoadVa();

      new Valine({
        el: "#vcomments",
        app_id: "vsanVmkaurLHyq2Xey9odo5V-gzGzoHsz",
        app_key: "v2hMMe4Mzz3mqpy9jwd7qx8B",
        placeholder: "说点什么吧！",
        path: window.location.pathname,
        avatar: "retro",
        meta: ["nick","mail","link"],
        pageSize: "10",
        lang: "zh-CN",
        highlight: true,
        recordIP: false,
        serverURLs: "",
      });
    };
  </script>
  <noscript>Please enable JavaScript to view the <a href="https://valine.js.org" target="_blank" rel="nofollow noopener noopener">comments
      powered by Valine.</a></noscript>


              </div>
            
          </div>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div id="tocbot"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    
  </main>

  
    <a id="scroll-top-button" href="#" role="button">
      <i class="iconfont icon-arrowup" aria-hidden="true"></i>
    </a>
  

  
    <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
  

  

  

  <footer class="mt-5">
  <div class="text-center py-3">
    <div>
      <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a>
      <i class="iconfont icon-love"></i>
      <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener">
        <span>Fluid</span></a>
    </div>
    
  <div class="statistics">
    
    

    
      
        <!-- LeanCloud 统计PV -->
        <span id="leancloud-site-pv-container" style="display: none">
            总访问量 
            <span id="leancloud-site-pv"></span>
             次
          </span>
      
      
        <!-- LeanCloud 统计UV -->
        <span id="leancloud-site-uv-container" style="display: none">
            总访客数 
            <span id="leancloud-site-uv"></span>
             人
          </span>
      

    
  </div>


    
  <!-- 备案信息 -->
  <div class="beian">
    <a href="http://beian.miit.gov.cn/" target="_blank"
       rel="nofollow noopener">京ICP证123456号</a>
    
      <a
        href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=12345678"
        rel="nofollow noopener"
        class="beian-police"
        target="_blank"
      >
        <span class="beian-police-sep">&nbsp;|&nbsp;</span>
        
          <img src="/binblog/img/police_beian.png" srcset="/binblog/img/loading.gif" alt="police-icon" />
        
        <span>京公网安备12345678号</span>
      </a>
     
  </div>


    
  </div>
</footer>

<!-- SCRIPTS -->
<script  src="https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js" ></script>
<script  src="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/js/bootstrap.min.js" ></script>
<script  src="/binblog/js/debouncer.js" ></script>
<script  src="/binblog/js/main.js" ></script>

<!-- Plugins -->


  
    <script  src="/binblog/js/lazyload.js" ></script>
  



  <script defer src="https://cdn.staticfile.org/clipboard.js/2.0.6/clipboard.min.js" ></script>
  <script  src="/binblog/js/clipboard-use.js" ></script>



  <script defer>
  (function () {
    // 查询存储的记录
    function getRecord(Counter, target) {
      return new Promise(function (resolve, reject) {
        Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify({target})))
          .then(resp => resp.json())
          .then(({results, code, error}) => {
            if (code === 401) {
              throw error;
            }
            if (results && results.length > 0) {
              var record = results[0];
              resolve(record);
            } else {
              Counter('post', '/classes/Counter', {target, time: 0})
                .then(resp => resp.json())
                .then((record, error) => {
                  if (error) {
                    throw error;
                  }
                  resolve(record);
                }).catch(error => {
                console.error('Failed to create', error);
                reject(error);
              });
            }
          }).catch((error) => {
          console.error('LeanCloud Counter Error:', error);
          reject(error);
        });
      })
    }

    // 发起自增请求
    function increment(Counter, incrArr) {
      return new Promise(function (resolve, reject) {
        Counter('post', '/batch', {
          "requests": incrArr
        }).then((res) => {
          res = res.json();
          if (res.error) {
            throw res.error;
          }
          resolve(res);
        }).catch((error) => {
          console.error('Failed to save visitor count', error);
          reject(error);
        });
      });
    }

    // 构建自增请求体
    function buildIncrement(objectId) {
      return {
        "method": "PUT",
        "path": `/1.1/classes/Counter/${ objectId }`,
        "body": {
          "time": {
            '__op': 'Increment',
            'amount': 1
          }
        }
      }
    }

    // 校验是否为有效的 UV
    function validUV() {
      var key = 'LeanCloud_UV_Flag';
      var flag = localStorage.getItem(key);
      if (flag) {
        // 距离标记小于 24 小时则不计为 UV
        if (new Date().getTime() - parseInt(flag) <= 86400000) {
          return false;
        }
      }
      localStorage.setItem(key, new Date().getTime().toString());
      return true;
    }

    function addCount(Counter) {
      var enableIncr = 'true' === 'true' && window.location.hostname !== 'localhost';
      var getterArr = [];
      var incrArr = [];

      // 请求 PV 并自增
      var pvCtn = document.querySelector('#leancloud-site-pv-container');
      if (pvCtn || enableIncr) {
        var pvGetter = getRecord(Counter, 'site-pv').then((record) => {
          incrArr.push(buildIncrement(record.objectId))
          var ele = document.querySelector('#leancloud-site-pv');
          if (ele) {
            ele.innerText = record.time + 1;
            if (pvCtn) {
              pvCtn.style.display = 'inline';
            }
          }
        });
        getterArr.push(pvGetter);
      }

      // 请求 UV 并自增
      var uvCtn = document.querySelector('#leancloud-site-uv-container');
      if (uvCtn || enableIncr) {
        var uvGetter = getRecord(Counter, 'site-uv').then((record) => {
          var vuv = validUV();
          vuv && incrArr.push(buildIncrement(record.objectId))
          var ele = document.querySelector('#leancloud-site-uv');
          if (ele) {
            ele.innerText = record.time + (vuv ? 1 : 0);
            if (uvCtn) {
              uvCtn.style.display = 'inline';
            }
          }
        });
        getterArr.push(uvGetter);
      }

      // 如果是文章，请求文章的浏览数，并自增
      if ('true' === 'true') {
        var viewCtn = document.querySelector('#leancloud-post-views-container');
        if (viewCtn || enableIncr) {
          var target = decodeURI('/binblog/2020/05/31/%E6%89%8B%E5%86%99%E7%B3%BB%E5%88%97%E4%B9%8B%E6%89%8B%E5%86%99%E4%B8%80%E4%B8%AAMyBatis/');
          var viewGetter = getRecord(Counter, target).then((record) => {
            incrArr.push(buildIncrement(record.objectId))
            if (viewCtn) {
              var ele = document.querySelector('#leancloud-post-views');
              if (ele) {
                ele.innerText = (record.time || 0) + 1;
                viewCtn.style.display = 'inline';
              }
            }
          });
          getterArr.push(viewGetter);
        }
      }

      // 如果启动计数自增，批量发起自增请求
      if (enableIncr) {
        Promise.all(getterArr).then(() => {
          incrArr.length > 0 && increment(Counter, incrArr);
        })
      }
    }

    var app_id = 'vsanVmkaurLHyq2Xey9odo5V-gzGzoHsz'
    var app_key = 'v2hMMe4Mzz3mqpy9jwd7qx8B'
    var server_url = ''

    function fetchData(api_server) {
      var Counter = (method, url, data) => {
        return fetch(`${ api_server }/1.1${ url }`, {
          method,
          headers: {
            'X-LC-Id': app_id,
            'X-LC-Key': app_key,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data)
        });
      };

      addCount(Counter);
    }

    var api_server = app_id.slice(-9) !== '-MdYXbMMI' ? server_url : `https://${ app_id.slice(0, 8).toLowerCase() }.api.lncldglobal.com`;

    if (api_server) {
      fetchData(api_server);
    } else {
      fetch('https://app-router.leancloud.cn/2/route?appId=' + app_id)
        .then(resp => resp.json())
        .then(({api_server}) => {
          fetchData('https://' + api_server);
        });
    }
  })();
</script>






  <script  src="https://cdn.staticfile.org/tocbot/4.11.1/tocbot.min.js" ></script>
  <script>
    $(document).ready(function () {
      var boardCtn = $('#board-ctn');
      var boardTop = boardCtn.offset().top;

      tocbot.init({
        tocSelector: '#tocbot',
        contentSelector: 'article.markdown-body',
        headingSelector: 'h1,h2,h3,h4,h5,h6',
        linkClass: 'tocbot-link',
        activeLinkClass: 'tocbot-active-link',
        listClass: 'tocbot-list',
        isCollapsedClass: 'tocbot-is-collapsed',
        collapsibleClass: 'tocbot-is-collapsible',
        collapseDepth: 0,
        scrollSmooth: true,
        headingsOffset: -boardTop
      });
      if ($('.toc-list-item').length > 0) {
        $('#toc').css('visibility', 'visible');
      }
    });
  </script>



  <script  src="https://cdn.staticfile.org/typed.js/2.0.11/typed.min.js" ></script>
  <script>
    var typed = new Typed('#subtitle', {
      strings: [
        '  ',
        "手写系列之手写一个MyBatis&nbsp;",
      ],
      cursorChar: "_",
      typeSpeed: 70,
      loop: false,
    });
    typed.stop();
    $(document).ready(function () {
      $(".typed-cursor").addClass("h2");
      typed.start();
    });
  </script>



  <script  src="https://cdn.staticfile.org/anchor-js/4.2.2/anchor.min.js" ></script>
  <script>
    anchors.options = {
      placement: "right",
      visible: "hover",
      
    };
    var el = "h1,h2,h3,h4,h5,h6".split(",");
    var res = [];
    for (item of el) {
      res.push(".markdown-body > " + item)
    }
    anchors.add(res.join(", "))
  </script>



  <script  src="/binblog/js/local-search.js" ></script>
  <script>
    var path = "/binblog/local-search.xml";
    var inputArea = document.querySelector("#local-search-input");
    inputArea.onclick = function () {
      searchFunc(path, 'local-search-input', 'local-search-result');
      this.onclick = null
    }
  </script>



  <script  src="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.css" />

  <script>
    $('#post img:not(.no-zoom img, img[no-zoom]), img[zoom]').each(
      function () {
        var element = document.createElement('a');
        $(element).attr('data-fancybox', 'images');
        $(element).attr('href', $(this).attr('src'));
        $(this).wrap(element);
      }
    );
  </script>





  

  
    <!-- MathJax -->
    <script>
      MathJax = {
        tex: {
          inlineMath: [['$', '$'], ['\\(', '\\)']]
        },
        options: {
          renderActions: {
            findScript: [10, doc => {
              document.querySelectorAll('script[type^="math/tex"]').forEach(node => {
                const display = !!node.type.match(/; *mode=display/);
                const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
                const text = document.createTextNode('');
                node.parentNode.replaceChild(text, node);
                math.start = { node: text, delim: '', n: 0 };
                math.end = { node: text, delim: '', n: 0 };
                doc.math.push(math);
              });
            }, '', false],
            insertedScript: [200, () => {
              document.querySelectorAll('mjx-container').forEach(node => {
                let target = node.parentNode;
                if (target.nodeName.toLowerCase() === 'li') {
                  target.parentNode.classList.add('has-jax');
                }
              });
            }, '', false]
          }
        }
      };
    </script>

    <script async src="https://cdn.staticfile.org/mathjax/3.0.5/es5/tex-svg.js" ></script>

  













  

  

  

  

  

  





<script src="/binblog/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"model":{"jsonPath":"/binblog/live2dw/assets/koharu.model.json"},"display":{"position":"right","width":150,"height":300},"mobile":{"show":true},"log":false,"pluginJsPath":"lib/","pluginModelPath":"assets/","pluginRootPath":"live2dw/","tagMode":false});</script></body>
</html>
